home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / web / literateprog / primes.web < prev    next >
Text File  |  1992-09-23  |  18KB  |  415 lines

  1. % limbo material
  2. \font\ninerm=amr9
  3. \let\mc=\ninerm % medium caps for names like PASCAL
  4. \def\WEB{{\tt WEB}}
  5. \def\PASCAL{{\mc PASCAL}}
  6. \def\[{\ifhmode\ \fi$[\![$}
  7. \def\]{$]\!]$\ }
  8. \def\<{$\langle\,$}
  9. \def\>{$\,\rangle$}
  10. \def\Dijk{{2}} % unnecessary when combined with text of paper
  11. \def\goto{{3}} % ditto
  12. \hyphenation{Dijk-stra} % ditto
  13. \def\sec{{\tensy x}}
  14. \hsize=84mm
  15.  
  16. @* Printing primes: An example of \WEB.
  17. The following program is essentially the same as Edsger Dijkstra's
  18. @^Dijkstra, Edsger@> ``first example of step-wise program composition,''
  19. found on pages 26--39 of his {\sl Notes on Structured Programming},$^{\Dijk}$
  20. but it has been translated into the \WEB\ language. @.WEB@>
  21.  
  22. \[Double brackets will be used in what follows to enclose comments
  23. relating to \WEB\ itself, because the chief purpose of this program
  24. is to introduce the reader to the \WEB\ style of documentation.
  25. \WEB\ programs are always broken into small sections, each
  26. of which has a serial number; the present section is number~1.\]
  27.  
  28. Dijkstra's program prints a table of the first thousand prime numbers.  We
  29. shall begin as he did, by reducing the entire program to its top-level
  30. description. \[Every section in a \WEB\ program begins with optional {\it
  31. commentary\/} about that section, and ends with optional {\it program
  32. text\/} for the section. For example, you are now reading part of the
  33. commentary in \sec1, and the program text for \sec1 immediately follows
  34. the present paragraph. Program texts are specifications of \PASCAL\
  35. programs; they either use \PASCAL\ language directly, or they use angle
  36. brackets to represent \PASCAL\ code that appears in other sections. For
  37. example, the angle-bracket notation `\X2:Program to print $\ldots$
  38. numbers\X' is \WEB's way of saying the following: ``The \PASCAL\ text to
  39. be inserted here is called `Program to print $\ldots$ numbers', and you
  40. can find out all about it by looking at section~2.'' One of the main
  41. characteristics of \WEB\ is that different parts of the program are
  42. usually abbreviated, by giving them such an informal top-level
  43. description.\]
  44.  
  45. @p @<Program to print the first thousand prime numbers@>
  46.  
  47. @ This program has no input, because we want to keep it rather simple.
  48. The result of the program will be to produce a list of the first
  49. thousand prime numbers, and this list will appear on the |output| file.
  50.  
  51. Since there is no input, we declare the value |m=1000| as a compile-time
  52. constant. The program itself is capable of generating the first
  53. |m| prime numbers for any positive |m|, as long as the computer's
  54. finite limitations are not exceeded.
  55.  
  56. \[The program text below specifies the ``expanded meaning'' of `\X2:Program
  57. to print $\ldots$ numbers\X'; notice that it involves the top-level
  58. descriptions of three other sections. When those top-level descriptions
  59. are replaced by their expanded meanings, a syntactically correct \PASCAL\
  60. program will be obtained.\]
  61.  
  62. @<Program to print...@>=
  63. program print_primes(output);
  64. const @!m=1000; @<Other constants of the program@>@;
  65. var @<Variables of the program@>@;
  66. begin @<Print the first |m| prime numbers@>;
  67. end.
  68.  
  69. @* Plan of the program.
  70. We shall proceed to fill out the rest of the program by making whatever
  71. decisions seem easiest at each step; the idea will be to strive for
  72. simplicity first and efficiency later, in order to see where this leads us.
  73. The final program may not be optimum, but we want it to be reliable,
  74. well motivated, and reasonably fast.
  75.  
  76. Let us decide at this point to maintain a table that includes all of the
  77. prime numbers that will be generated, and to separate the generation
  78. problem from the printing problem.
  79.  
  80. \[The \WEB\ description you are reading once again follows a pattern that
  81. will soon be familiar: A typical section begins with comments and
  82. ends with program text. The comments motivate and explain noteworthy
  83. features of the program text.\]
  84.  
  85. @<Print the first...@>=
  86. @<Fill table |p| with the first |m| prime numbers@>;
  87. @<Print table |p|@>
  88.  
  89. @ How should table |p| be represented? Two possibilities suggest
  90. themselves: We could construct a sufficiently large array
  91. of boolean values in which the $k$th entry is |true| if and only if the
  92. number~|k| is prime; or we could build an array of integers in which
  93. the |k|th entry is the |k|th prime number. Let us choose the latter
  94. alternative, by introducing an integer array called |p[1..m]|.
  95.  
  96. In the documentation below, the notation `|p[k]|' will refer to the
  97. |k|th element of array~|p|, while `$p_k$' will refer to the $k$th
  98. prime number. If the program is correct, |p[k]| will either be
  99. equal to $p_k$ or it will not yet have been assigned any value.
  100.  
  101. \[Incidentally, our program will eventually make use of several
  102. more variables as we refine the data structures. All of the sections
  103. where variables are declared will be called `\X4:Variables of the
  104. program\X'; the number `{\eightrm4}' in this name refers to the
  105. present section, which is the first section to specify the
  106. expanded meaning of `\<Variables of the program\>'.
  107. The note `{\eightrm See also $\ldots$}' refers to all of the other
  108. sections that have the same top-level description. The expanded meaning of
  109. `\X4:Variables of the program\X' consists of all the program texts
  110. for this name, not just the text found in~\sec4.\]
  111.  
  112. @<Variables...@>=@!p:array[1..m] of integer;
  113.   {the first |m| prime numbers, in increasing order}
  114.  
  115. @* The output phase.
  116. Let's work on the second part of the program first. It's not as interesting
  117. as the problem of computing prime numbers; but the job of printing must be
  118. done sooner or later, and we might as well do it sooner, since it will
  119. be good to have it done. \[And it is easier to learn \WEB\ when reading a
  120. program that has comparatively few distracting complications.\]
  121.  
  122. Since |p| is simply an array of integers, there is little difficulty
  123. in printing the output, except that we need to decide upon a suitable
  124. output format. Let us print the table on separate pages, with |rr| rows
  125. and |cc| columns per page, where every column is |ww| character positions
  126. wide. In this case we shall choose |rr=50|, |cc=4|, and |ww=10|, so that
  127. the first 1000 primes will appear on five pages. The program will
  128. not assume that |m| is an exact multiple of $|rr|\cdot|cc|$.
  129. @^output format@>
  130.  
  131. @<Other constants...@>=
  132. @!rr=50; {this many rows will be on each page in the output}
  133. @!cc=4; {this many columns will be on each page in the output}
  134. @!ww=10; {this many character positions will be used in each column}
  135.  
  136. @ In order to keep this program reasonably free of notations that
  137. are uniquely \PASCAL esque, \[and in order to illustrate more of the
  138. facilities of \WEB,\] a few macro definitions for low-level output
  139. instructions are introduced here. All of the output-oriented commands
  140. in the remainder of the program will be stated in terms of five
  141. simple primitives called |print_string|, |print_integer|, |print_entry|,
  142. |new_line|, and |new_page|.
  143.  
  144. \[Sections of a \WEB\ program are allowed to contain {\it macro definitions\/}
  145. between the opening comments and the closing program text. The
  146. general format for each section is actually tripartite: commentary,
  147. then definitions, then program. Any of the three parts may be absent;
  148. for example, the present section contains no program text.\]
  149.  
  150. \[Simple macros simply substitute a bit of \PASCAL\ code for an
  151. identifier. Parametric macros are similar, but they also substitute
  152. an argument wherever `\#' occurs in the macro definition. The first three
  153. macro definitions here are parametric; the other two are simple.\]
  154.  
  155. @d print_string(#)==write(#) {put a given string into the |output| file}
  156. @d print_integer(#)==write(#:1) {put a given integer into the |output|
  157.     file, in decimal notation, using only as many digit positions as necessary}
  158. @d print_entry(#)==write(#:ww) {like |print_integer|, but |ww| character
  159.     positions are filled, inserting blanks at the left}
  160. @d new_line==write_ln {advance to a new line in the |output| file}
  161. @d new_page==page {advance to a new page in the |output| file}
  162.  
  163. @ Several variables are needed to govern the output process. When we begin
  164. to print a new page, the variable |page_number| will be the ordinal number
  165. of that page, and |page_offset| will be such that |p[page_offset]| is the
  166. first prime to be printed. Similarly, |p[row_offset]| will be the first
  167. prime in a given row.
  168.  
  169. \[Notice the notation `$+\S$' below; this indicates that the present
  170. section has the same name as a previous section, so the program text
  171. will be appended to some text that was previously specified.\]
  172.  
  173. @<Variables...@>=
  174. @!page_number:integer; {one more than the number of pages printed so far}
  175. @!page_offset:integer; {index into |p| for the first entry on the current page}
  176. @!row_offset:integer; {index into |p| for the first entry in the current row}
  177. @!c:0..cc; {runs through the columns in a row}
  178.  
  179. @ Now that appropriate auxiliary variables have been introduced, the process
  180. of outputting table~|p| almost writes itself.
  181.  
  182. @<Print table |p|@>=
  183. begin page_number:=1; page_offset:=1;
  184. while page_offset<=m do
  185.   begin @<Output a page of answers@>;
  186.   page_number:=page_number+1;
  187.   page_offset:=page_offset+rr*cc;
  188.   end;
  189. end
  190.  
  191. @ A simple heading is printed at the top of each page.
  192. @^output format@> @^page headings@>
  193.  
  194. @<Output a page of answers@>=
  195. begin print_string('The First ');
  196. print_integer(m);@/
  197. print_string(' Prime Numbers --- Page ');
  198. print_integer(page_number);
  199. new_line; new_line; {there's a blank line after the heading}
  200. for row_offset:=page_offset to page_offset+rr-1 do
  201.   @<Output a line of answers@>;
  202. new_page;
  203. end
  204.  
  205. @ The first row will contain
  206. $$\hbox{|p[1]|, |p[1+rr]|, |p[1+2*rr]|, \dots;}$$
  207. a similar pattern holds for each value of the |row_offset|.
  208.  
  209. @<Output a line of answers@>=
  210. begin for c:=0 to cc-1 do
  211.   if row_offset+c*rr<=m then print_entry(p[row_offset+c*rr]);
  212. new_line;
  213. end
  214.  
  215. @* Generating the primes.
  216. The remaining task is to fill table~|p| with the correct numbers.
  217. Let us do this by generating its entries one at a time: Assuming that
  218. we have computed all primes that are |j|~or less, we will advance |j|
  219. to the next suitable value, and continue doing this until the
  220. table is completely full.
  221.  
  222. The program includes a provision to initialize the variables in certain
  223. data structures that will be introduced later.
  224.  
  225. @<Fill table |p|...@>=
  226. @<Initialize the data structures@>;
  227. while k<m do
  228.   begin @<Increase |j| until it is the next prime number@>;
  229.   k:=k+1; p[k]:=j;
  230.   end
  231.  
  232. @ We need to declare the two variables |j| and~|k| that were just
  233. introduced.
  234.  
  235. @<Variables...@>=
  236. @!j:integer; {all primes |<=j| are in table |p|}
  237. @!k:0..m; {this many primes are in table |p|}
  238.  
  239. @ So far we haven't needed to confront the issue of what a prime number
  240. is. But everything else has been taken care of, so we must delve into
  241. a bit of number theory now.
  242.  
  243. By definition, a number is called prime if it is an integer greater
  244. than~1 that is not evenly divisible by any smaller prime number. Stating
  245. this another way, the integer |j>1| is not prime if and only if there
  246. exists a prime number $p_n<j$ such that |j| is a multiple of~$p_n$.
  247. @^prime number, definition of@>
  248.  
  249. Therefore the section of the program that is called `\<Increase |j| until
  250. it is the next prime number\>' could be coded very simply:
  251. `\ignorespaces|repeat j:=j+1;|\unskip\
  252. \<Give to~|j_prime| the meaning: |j|~is a prime number\>;
  253. \ignorespaces|until j_prime|\unskip'.
  254. And to compute the boolean value |j_prime|, the following
  255. would suffice: `\ignorespaces|j_prime:=true; for n:=1 to k do|\unskip\
  256. \<If |p[n]| divides |j|, set |j_prime:=false|\>'.
  257.  
  258. @ However, it is possible to obtain a much more efficient algorithm by
  259. using more facts of number theory. In the first place, we can speed
  260. things up a bit by recognizing that $p_1=2$ and that all subsequent
  261. primes are odd; therefore we can let |j| run through odd values only.
  262. Our program now takes the following form:
  263.  
  264. @<Increase |j| until...@>=
  265. repeat j:=j+2; @<Update variables that depend on~|j|@>;
  266. @<Give to |j_prime| the meaning: |j|~is a prime number@>;
  267. until j_prime
  268.  
  269. @ The |repeat| loop in the previous section introduces a boolean
  270. variable |j_prime|, so that it will not be necessary to resort to
  271. a |goto| statement. (We are following Dijkstra,$^\Dijk$ not Knuth.$^\goto$)
  272. @^Dijkstra, Edsger@> @^Knuth, Donald E.@>
  273.  
  274. @<Variables...@>=
  275. @!j_prime:boolean; {is |j| a prime number?}
  276.  
  277. @ In order to make the odd-even trick work, we must of course initialize
  278. the variables |j|, |k|, and |p[1]| as follows.
  279.  
  280. @<Init...@>=
  281. j:=1; k:=1; p[1]:=2;
  282.  
  283. @ Now we can apply more number theory in order to obtain further
  284. economies. If |j| is not prime, its smallest prime factor $p_n$ will
  285. be $\sqrt j$ or less. Thus if we know a number |ord| such that
  286. $$p[|ord|]^2>j,$$ and if |j| is odd, we need only test for divisors
  287. in the set $\{p[2], \ldots, p[|ord|-1]\}$. This is much faster than
  288. testing divisibility by $\{p[2],\ldots,p[k]\}$, since |ord| tends
  289. to be much smaller than~|k|. \ (Indeed, when |k| is large, the
  290. celebrated ``prime number theorem'' implies that the value of |ord|
  291. will be approximately $2\sqrt{k/\!\ln k}$.)
  292.  
  293. Let us therefore introduce |ord| into the data structure. A moment's
  294. thought makes it clear that |ord| changes in a simple way when |j|
  295. increases, and that another variable |square| facilitates the
  296. updating process.
  297.  
  298. @<Variables...@>=
  299. @!ord:2..ord_max; {the smallest index |>=2| such that $p_{ord}^2>j$}
  300. @!square:integer; {$|square|=p_{ord}^2$}
  301.  
  302. @ @<Init...@>=
  303. ord:=2; square:=9;
  304.  
  305. @ The value of |ord| will never get larger than a certain value
  306. |ord_max|, which must be chosen sufficiently large. It turns out that
  307. |ord| never exceeds~30 when |m=1000|.
  308.  
  309. @<Other const...@>=
  310. @!ord_max=30; {$p_{ord\_max}^2$ must exceed $p_m$}
  311.  
  312. @ When |j| has been increased by~2, we must increase |ord| by unity
  313. when $j=p_{ord}^2$, i.e., when |j=square|.
  314.  
  315. @<Update variables that depend on~|j|@>=
  316. if j=square then
  317.   begin ord:=ord+1;
  318.   @<Update variables that depend on~|ord|@>;
  319.   end
  320.  
  321. @ At this point in the program, |ord| has just been increased by unity,
  322. and we want to set $|square|:=p_{ord}^2$. A surprisingly subtle point
  323. arises here: How do we know that $p_{ord}$ has already been computed,
  324. i.e., that |ord<=k|? If there were a gap in the sequence of prime numbers,
  325. such that $p_{k+1}>p_k^2$ for some~$k$, then this part of the program would
  326. refer to the yet-uncomputed value |p[k+1]| unless some special test were
  327. made.
  328.  
  329. Fortunately, there are no such gaps. But no simple proof of this fact is
  330. known. For example, Euclid's famous demonstration that there are
  331. infinitely many prime numbers is strong enough to prove only that
  332. $p_{k+1}<=p_1\ldots p_k+1$. Advanced books on number theory come to our
  333. rescue by showing that much more is true; for example, ``Bertrand's
  334. postulate'' @^Bertrand, Joseph, postulate@> states that $p_{k+1}<2p_k$
  335. for all~$k$.
  336.  
  337. @<Update variables that depend on~|ord|@>=
  338. square:=p[ord]*p[ord]; {at this point |ord<=k|}
  339.  
  340. @* The inner loop.
  341. Our remaining task is to determine whether or not a given integer~|j| is prime.
  342. The general outline of this part of the program is quite simple,
  343. using the value of |ord| as described above.
  344.  
  345. @<Give to |j_prime|...@>=
  346. n:=2; j_prime:=true;
  347. while (n<ord) and j_prime do
  348.   begin @<If |p[n]| is a factor of~|j|, set |j_prime:=false|@>;
  349.   n:=n+1;
  350.   end
  351.  
  352. @ @<Var...@>=
  353. @!n:2..ord_max; {runs from 2 to |ord| when testing divisibility}
  354.  
  355. @ Let's suppose that division is very slow or nonexistent on our
  356. machine. We want to detect nonprime odd numbers, which are odd multiples
  357. of the set of primes $\{p_2,\ldots,p_{ord}\}$.
  358.  
  359. Since |ord_max| is small, it is reasonable to maintain an auxiliary table of
  360. the smallest odd multiples that haven't already been used to show that
  361. some~|j| is nonprime. In other words, our goal is to ``knock out'' all
  362. of the odd multiples of each $p_n$ in the set $\{p_2,\ldots,p_{ord}\}$,
  363. and one way to do this is to introduce an auxiliary table that serves as
  364. a control structure for a set of knock-out procedures that are being
  365. simulated in parallel. (The so-called ``sieve of Eratosthenes''
  366. @^Eratosthenes, sieve of@> generates primes by a similar method, but
  367. it knocks out the multiples of each prime serially.)
  368.  
  369. The auxiliary table suggested by these considerations is a |mult|
  370. array that satisfies the following invariant condition: For |2<=n<ord|,
  371. |mult[n]| is an odd multiple of $p_n$ such that $|mult|[n]<j+2p_n$.
  372.  
  373. @<Var...@>=
  374. @!mult:array[2..ord_max] of integer; {runs through multiples of primes}
  375.  
  376. @ When |ord| has been increased, we need to initialize a new element of
  377. the |mult| array. At this point $j=p[|ord|-1]^2$, so there is no
  378. need for an elaborate computation.
  379.  
  380. @<Update variables that depend on~|ord|@>=
  381. mult[ord-1]:=j;
  382.  
  383. @ The remaining task is straightforward, given the data structures
  384. already prepared. Let us recapitulate the current situation: The
  385. goal is to test whether or not |j|~is divisible by~$p_n$, without
  386. actually performing a division. We know that $j$~is odd, and that
  387. |mult[n]| is an odd multiple of~$p_n$ such that $|mult|[n]<j+2p_n$.
  388. If |mult[n]<j|, we can increase |mult[n]| by $2p_n$ and the same
  389. conditions will hold. On the other hand if |mult[n]>=j|, the
  390. conditions imply that |j|~is divisible by~$p_n$ if and only if
  391. |j=mult[n]|.
  392.  
  393. @<If...@>=
  394. while mult[n]<j do mult[n]:=mult[n]+p[n]+p[n];
  395. if mult[n]=j then j_prime:=false
  396.  
  397. @* Index.
  398. Every identifier used in this program is shown here together with a list
  399. of the section numbers where that identifier appears. The section number
  400. is underlined if the identifier was defined in that section. However,
  401. one-letter identifiers are indexed only at their point of definition,
  402. since such identifiers tend to appear almost everywhere. \[An index like
  403. this is prepared automatically by the \WEB\ software, and it is appended
  404. to the final section of the program. However, underlining of section
  405. numbers is not automatic; the user is supposed to mark identifiers
  406. at their point of definition in the \WEB\ source file.\]
  407.  
  408. This index also refers to some of the places where key elements of the
  409. program are treated. For example, the entries for `Output format' and
  410. `Page headings' indicate where details of the output format are
  411. discussed. Several other topics that appear in the documentation
  412. (e.g., `Bertrand's postulate') have also been indexed. \[Special
  413. instructions within a \WEB\ source file can be used to insert
  414. essentially anything into the index.\]
  415.